package aash.catalog.models;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.event.EventListenerList;

import org.apache.log4j.Logger;

import aash.catalog.event.ExplorerListener;
import aash.catalog.event.PropertyChangedEvent;
import aash.utils.database.Database;
import aash.utils.element.CatalogProperty;

/**
 * MVC Model for AashCatalog Project.
 * @author Aashintan
 */
public class CatalogModel {
    /**
     * Logger.
     */
    private static Logger log = Logger.getLogger(CatalogModel.class.getName());
    /**
     * Table containing translations.
     */
    private final ResourceBundle table = ResourceBundle.getBundle("aash.catalog.general");
    /**
     * Fields, by name.
     */
    private ArrayList < String > fields;
    /**
     * Database.
     */
    private Database db;
    /**
     * Databases directory path
     */
    private String dbDirectoryPath = "databases";
    /**
     * ID of the current line in the DB.
     */
    private Integer id;
    /**
     * ID max present in table AASH.PRINCIPAL.
     */
    private Integer idMax;
    /**
     * List of elements in the DB (Lines of properties and picture's id).
     */
    private HashMap < String, CatalogProperty > lines;
    /**
     * .
     */
    private final EventListenerList listeners;

    /**
     * CatalogueModel class constructor.
     */
    public CatalogModel() {
        super();
        fields = new ArrayList < String >();
        listeners = new EventListenerList();
        fillFields();
        id = 1;
        idMax = 0;
    }

    /**
     * Add an ExplorerListener to the Model.
     * @param listener the listener to add
     */
    public final void addExplorerListener(final ExplorerListener listener) {
        listeners.add(ExplorerListener.class, listener);
    }

    /**
     * Close current connection.
     */
    public final void closeDB() {
        if (db != null) {
            db.close();
        }
    }

    /**
     * Connect to DataBase.
     * @param stName of the database
     */
    public final void connectDB(final String stName) {
    	File f = new File(dbDirectoryPath + "\\" + stName);
    	if(f.exists()) {
	        db = new Database();
	        db.connectDB(stName);
	        db.open();
	        id = 1;
	        idMax = db.getIdMax();
    	} else {
    		log.error("Attempt to connect to an inexistent database.");
    	}
    }

    /**
     * Throws changes to observers.
     * @param stKey of the property
     */
    public final void firePropertyChanged(final String stKey) {
        ExplorerListener[] listenerList = listeners.getListeners(ExplorerListener.class);
        for (ExplorerListener listener : listenerList) {
            listener.propertyChanged(new PropertyChangedEvent(this, stKey, getProperty(stKey)));
        }
    }

    /**
     * Format the current DB.
     */
    public final void formatDb() {
        String[] columnNames = fields.toArray(new String[fields.size()]);
        db.format(columnNames);
        id = 1;
        idMax = 0;
    }

    /**
     * @return list of available databases
     */
    public final String[] getDbList() {
        File[] files = null;
        File directoryToScan = new File(dbDirectoryPath);
        files = directoryToScan.listFiles();
        int i = files.length;
        String[] dbList = new String[i];
        while (i != 0) {
            dbList[i - 1] = files[i - 1].getName();
            i--;
        }
        return dbList;
    }

    /**
     * @return the name of the current opened database
     */
    public final String getDbName() {
        String dbName = db.getName();
        return dbName;
    }

    /**
     * Return the fields available.
     * @return the ArrayList
     */
    public final ArrayList < String > getFields() {
        return fields;
    }

    /**
     * @return the current ID.
     */
    public final Integer getId() {
        return id;
    }

    /**
     * Return the lines of property.
     * @return the HashMap
     */
    public final HashMap < String, CatalogProperty > getLines() {
        return lines;
    }

    /**
     * Return the stKey associated property.
     * @param stKey of the property
     * @return the property or null if not found
     */
    public final CatalogProperty getProperty(final String stKey) {
        if (lines.containsKey(stKey)) {
            return lines.get(stKey);
        } else {
            return null;
        }
    }
    
    /**
     * Initialize the databases directory.
     */
    public void initDbDirectory() {
    	File f = new File(dbDirectoryPath);
    	if(f.exists()) {
    		if(!f.isDirectory()) {
    			f.delete();
    			f.mkdir();
    		}
    	} else {
    		f.mkdir();
    	}
    }

    /**
     * @return true if database is connected
     */
    public final boolean isDBconnected() {
        boolean isDBconnected = false;
        if ((db != null) && db.isOpen()) {
            isDBconnected = true;
        }
        return isDBconnected;
    }

    /**
     * Display the backward picture in the list.
     */
    public final void listBackward() {
    }

    /**
     * Display the forward picture in the list.
     */
    public final void listForward() {
    }

    /**
     * Return the properties file containing initializing data.
     * @param stName of the file
     * @return the file
     */
    public final Properties loadPropertiesFromFile(final String stName) {
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream(stName));
        } catch (FileNotFoundException e) {
            log.error("File config.properties not found. " + e);
            return null;
        } catch (IOException e) {
            log.error("Input exception. " + e);
            return null;
        }
        return prop;
    }

    /**
     * Return the properties file containing initializing data.
     * @param stName of the file
     * @return the file
     */
    public final Properties loadPropertiesFromXMLFile(final String stName) {
        Properties prop = new Properties();
        try {
            prop.loadFromXML(new FileInputStream(stName));
        } catch (FileNotFoundException e) {
            log.error("File config.properties not found. " + e);
            return null;
        } catch (IOException e) {
            log.error("Input exception. " + e);
            return null;
        }
        return prop;
    }

    /**
     * Initiate DataBase.
     * @param stName of the database
     */
    public final void openDB(final String stName) {
        db = new Database(stName);
        db.open();
        /*
         * db.executeUpdate("create table AASH.ii_pic_pictures (pic_id integer not null, " +
         * "pic_name char(50), primary key (pic_id))");
         * db.executeUpdate("insert into AASH.ii_pic_pictures values(1,'machin')");
         * db.executeUpdate("insert into AASH.ii_pic_pictures values(2,'truc')");
         */
        db.getTables();
        System.out.println("-----------------------");
        // System.out.println(db.execute("select * from AASH.ii_pic_pictures"));
        // db.executeUpdate("drop table AASH.ii_pic_pictures");
    }

    /**
     * Add an ExplorerListener to the Model.
     * @param listener the listener to add
     */
    public final void removeExplorerListener(final ExplorerListener listener) {
        listeners.remove(ExplorerListener.class, listener);
    }

    /**
     * Save informations.
     * @param stAttr list of values to save
     */
    public final void save(final String[] stAttr) {
        if (id.compareTo(idMax) > 0) {
            db.insertLine(stAttr, idMax + 1);
            idMax++;
            id = idMax;
        } else {
            db.updateLine(fields.toArray(new String[fields.size()]), stAttr, id);
        }
    }

    /**
     * Set the current ID.
     * @param iId of the property
     */
    public final void setID(final int iId) {
        id = iId;
        if (idMax > 0) {
            updateFields();
        }
    }

    /**
     * Set a value to a property.
     * @param stKey of the property
     * @param stValue of the property
     */
    public final void setProperty(final String stKey, final String stValue) {
        if (lines.containsKey(stKey)) {
            lines.get(stKey).setValue(stValue);
            firePropertyChanged(stKey);
        }
    }

    /**
     * Store the properties to XML file.
     * @param pProp to store
     * @param stName of the file
     */
    public final void storePropertiesToXMLFile(final Properties pProp, final String stName) {
        try {
            pProp.storeToXML(new FileOutputStream(stName), "Configuration file");
        } catch (FileNotFoundException e) {
            log.error("File config.properties not found. " + e);
        } catch (IOException e) {
            log.error("Output exception. " + e);
        }
    }

    /**
     * Add a field in the Model.
     * @param stKey of the field.
     */
    private void addField(final String stKey) {
        lines.put(stKey, new CatalogProperty(table.getString("field." + stKey)));
    }

    /**
     * Fill the fields of the object lines.
     */
    private void fillFields() {
        fields.add("name");
        fields.add("group");
        fields.add("origin");
        fields.add("page");
        fields.add("class");
        lines = new HashMap < String, CatalogProperty >();
        int i = 0;
        while (i < fields.size()) {
            addField(fields.get(i));
            i++;
        }
    }

    /**
     * Update the current fields data.
     * @param stKey of the field
     * @param stValue of the field
     */
    private void modifyField(final String stKey, final String stValue) {
        CatalogProperty cp = lines.get(stKey);
        cp.setValue(stValue);
        lines.put(stKey, cp);
    }

    /**
     * Update the current fields data.
     */
    private void updateFields() {
        // Get the line/column names in an hashMap
        HashMap < String, String > currentLine = null;
        currentLine = db.getLineById(id);
        if (currentLine != null) {
            // Update the fields with
            Set < String > fieldNames = currentLine.keySet();
            Iterator < String > it = fieldNames.iterator();
            while (it.hasNext()) {
                // Get key (column name) and value
                String key = it.next();
                String value = currentLine.get(key);
                // Set them
                modifyField(key, value);
            }
        } else {
            log.info("Table empty.");
        }
    }
}
